#include <iostream>
#include <sstream>
#include <string>
#include <map>

using namespace std;

#include "nodo.h"

/****************************************************************
etiqEpilogo es utilizado por prologo y epilogo para la generación
de la etiqueta de dirección del epílogo.
****************************************************************/
static string etiqEpilogo = "";


/****************************************************************
prologo escribe el prólogo de una función, tomando como parámetro 
la entrada en la tabla de símbolos que pertenece a dicha función.

prologo es llamada por "definicion_de_funcion", y se
le envía como parámetro la entrada generada en "nombre_funcion"
****************************************************************/
void prologo(Entrada e) {
  cout << "\t.text\n.global " << e.nombre << endl
       << ".type " << e.nombre << ",@function" << endl
       << e.nombre << ":" << endl
       << "\tpushl\t%ebp" << endl
       << "\tmovl\t%esp, %ebp" << endl
       << "\tsubl\t$" << (tabla.getNumLocales()*4) << ",%esp" << endl;

  etiqEpilogo = ".epilogo_" + e.nombre;
}


/****************************************************************
epilogo escribe el epilogo de una función al final de 
"definicion_de_funcion".

Se utiliza la variable global etiqEpilogo para marcar la dirección 
del epílogo en el código ensamblador. Esta etiqueta es usada 
posteriormente por los nodos de salto de retorno de función
(NodoRetorno y NodoRetornoVacio)
****************************************************************/
void epilogo(Entrada e) {
  cout << etiqEpilogo << ":" << endl
       << "\tmovl\t%ebp, %esp" << endl
       << "\tpopl\t%ebp" << endl
       << "\tret" << endl;
}

/****************************************************************
Provee la funcionalidad de la generación de etiquetas secuenciales
****************************************************************/

int Nodo::cont = 0;

string Nodo::etiqueta(void) {
  ostringstream os;

  os << ".Lbl" << cont++;
  return string(os.str());
}


/****************************************************************
Genera o muestra secuencialmente los nodos del arbol de la secuencia
****************************************************************/
void NodoSecuencia::generar(void)
{
  sec->generar();
  secs->generar();
}


/****************************************************************
Genera el nodo if.
Tras generar la condición, su resultado queda
almacenado en en al acumulador. 
Se considera cierta la condición si su resultado es distinto de 
cero. Es decir, que se produce el salto si el resultado es igual
a cero. 
****************************************************************/
void NodoIf::generar(void)
{
  string etq_if = etiqueta();

  cond->generar();
  cout << "\tcmpl\t$0, %eax" << endl
       << "\tje\t" << etq_if << endl;
  cierto->generar();
  cout << etq_if << ":" << endl;		
}


/****************************************************************
Genera el nodo if-else.
Tras generar la condición, su resultado queda
almacenado en en al acumulador. 
Se considera cierta la condición si su resultado es distinto de 
cero. Es decir, que se produce el salto condicional si el resultado es igual
a cero. Se produce el salto incondicional al primer bloque en caso 
contrario.
****************************************************************/
void NodoIfElse::generar(void)
{
  string etq_if = etiqueta();
  string etq_else = etiqueta();

  cond->generar();
  cout << "\tcmpl\t$0, %eax" << endl
       << "\tje\t"  << etq_else << endl
       << "\tjmp\t" << etq_if << endl
       << etq_if << ":" << endl;
  cierto->generar();
  cout << etq_else << ":" << endl;
  falso->generar();
}


/****************************************************************
Genera el nodo de retorno con devolución de valor.

Tras generar la expresión a devolver, su resultado 
qued almacenado en en al acumulador.
 
A continuación se produce un salto incondicional al epílogo de la
función.
****************************************************************/
void NodoRetorno::generar(void)
{
  exp->generar();
  cout << "\tjmp\t" << etiqEpilogo << endl;
}


/****************************************************************
Genera el nodo de retorno sin devolución.
 
Simplemente se produce un salto incondicional al epílogo de la
función.
****************************************************************/
void NodoRetornoVacio::generar(void)
{
  cout << "\tjmp\t" << etiqEpilogo << endl;
}



/****************************************************************
Genera el nodo asignación
 
Tras generar la parte izquierda, el resultado almacenado en %eax
es la dirección efectiva donde hay que almacenar el resultdo de
la parte derecha.

Empujamos en la pila el valor de la dirección, y generamos 
la parte derecha.

Sacamos de la pila la dirección donde hay que almacenar el valor, 
colocandola en %ebx, y finalmente, movemos el valor resultante
(actualmente en %eax) en la dirección de la parte izquierda.
****************************************************************/
void NodoAsignacion::generar(void)
{
    izq->generar();
    cout << "\tpushl\t%eax" << endl;
    expr->generar();
    cout << "\tpopl\t%ebx" << endl;
    cout << "\tmovl\t%eax, 0(%ebx)" << endl;
}



/****************************************************************
Genera el nodo suma
 
Tras generar la parte derecha, el resultado está almacenado en %eax.

Empujamos en la pila el valor de resultante.

Generamos la parte izquierda, quedando el valor en %eax.

Recuperamos lo guardado en la pila en %ebx, y realizamos la suma
de %ebx sobre %eax, quedando el resultado en %eax.
****************************************************************/
void NodoSuma::generar(void) {
  der->generar();
  cout << "\tpushl\t%eax\n";
  izq->generar();
  cout << "\tpopl\t%ebx\n";
  cout << "\taddl\t%ebx, %eax" << endl;
}


/****************************************************************
Genera el nodo resta.

Utiliza el mismo esquema que el nodo suma, pero realizando la resta
en lugar de la suma
****************************************************************/
void NodoResta::generar(void) {
  izq->generar();
  cout << "\tpushl\t%eax\n";
  der->generar();
  cout << "\tmovl\t%eax, %ebx\n";
  cout << "\tpopl\t%eax\n";
  cout << "\tsubl\t%ebx, %eax" << endl;
}



/****************************************************************
Genera el nodo multiplicacion.

Utiliza el mismo esquema que el nodo suma, pero realizando la
multiplicación en lugar de la suma
****************************************************************/
void NodoMult::generar(void) {
  der->generar();
  cout << "\tpushl\t%eax\n";
  izq->generar();
  cout << "\tpopl\t%ebx\n";
  cout << "\timull\t%ebx, %eax" << endl;
}




/****************************************************************
Genera el nodo división.

Utiliza el mismo esquema que el nodo suma, pero realizando la
división en lugar de la suma
****************************************************************/
void NodoDivision::generar(void) {
  der->generar();
  cout << "\tpushl\t%eax\n";
  izq->generar();
  cout << "\tpopl\t%ebx\n";
  cout << "\tcdq\n";
  cout << "\tdivl\t%ebx" << endl;
}


/****************************************************************
Genera el nodo modulo.

Utiliza el mismo esquema que el nodo división, pero colocando el 
valor de %edx en el acumulador antes de salir.
****************************************************************/
void NodoModulo::generar(void) {
  der->generar();
  cout << "\tpushl\t%eax\n";
  izq->generar();
  cout << "\tpopl\t%ebx\n";
  cout << "\tcdq\n";
  cout << "\tdivl\t%ebx\n";
  cout << "\tmovl\t%edx, %eax" << endl;
}



/****************************************************************
Destructor del nodo llamada.

Los punteros de ListaParametros no son manejados por un recolector 
de basura. Por tanto, los objetos de la lista han de ser destruidos
antes de la destrucción de la lista.
****************************************************************/
NodoLlamada::~NodoLlamada() {
  for (ListaParametros::iterator it = params->begin();
       it != params->end(); ++it) 
    {
      delete *it;
    }
}

/****************************************************************
Genera la llamada a una función

Comienza obteniendo los valores a pasar como parámetros en orden 
inveso, y empujandolos en la pila.

Se produce la llamada, y se produce la recuperación de la pila
en una cantidad de memoria igual al tamaño de la pila utilizado
por los parámetros.
****************************************************************/
void NodoLlamada::generar(void) {
  for (ListaParametros::reverse_iterator it = params->rbegin();
       it != params->rend(); ++it) 
    {
      (*it)->generar();
      cout << "\tpushl\t%eax" << endl;
    }

  cout << "\tcall\t" << *nom << endl;
  cout << "\taddl\t$" << (params->size()*4) << ",%esp" << endl;
}



/****************************************************************
Genera el resultado del nodo constante entera, dejando disponible 
en el acumulador el valor expresado por el valor constante almacenado
en el nodo.
****************************************************************/
void NodoConstanteEntera::generar(void) {
  cout << "\tmovl\t$" << num << ", %eax\n";
}



/****************************************************************
Inicializa a cero el contador de constantes de cadenas
****************************************************************/
int NodoConstanteCadena::cont = 0;


map <string,int> NodoConstanteCadena::tablaCadenas;


/****************************************************************
La generación del NodoConstanteCadena consiste en mover al acumulador la dirección
donde está almacenada la cadena el en segmento .rodata
****************************************************************/
void NodoConstanteCadena::generar(void) {
  cout << "\tmovl\t$.S" << etiqueta << ", %eax\n";
}




/****************************************************************
La generación delNodoIdentificadorLVal consiste en mover al acumulador la dirección
efectiva, en caso de tratarse de almacenamiento dinámico, o directamente
mover la dirección en caso de tratarse de almacenamiento estático.
****************************************************************/
void NodoIdentificadorLVal::generar(void) {
  if (e.alm != ESTATICO) {
    cout << "\tleal\t" << e.direccion << ", %eax" << endl;
  } else {
    cout << "\tmovl\t" << e.direccion << ", %eax" << endl;
  }
}


void NodoVacio::generar(void) {
  // vacio
}



/****************************************************************
EL nodo menos unario sencillamente multiplica el acumulador por -1
****************************************************************/
void NodoMenosUnario::generar(void) {
  n->generar();  cout << "\tnegl\t%eax" << endl;  
}



/****************************************************************
EL nodo mas unario No realiza ninguna operacion, simplemente pasa
el valor al siguiente nivel.
****************************************************************/
void NodoMasUnario::generar(void) {
  n->generar();  
}



/****************************************************************
La generación del nodoInc prefijo comienza generando la parte_izquierda,
que deja una dirección efectiva (la de la "variable") en el acumulador.

Guardamos el valor de dicha dirección en la pila.

Movemos el valor de la variable al acumulador, y lo incrementamos
a uno.

Recuperamos la dirección de la parte_izquierda de la pila al registro %ebx,
y movemos el valor de %eax a dicha dirección. 
****************************************************************/
void NodoIncPrefijo::generar(void) {
  
  n->generar();
  cout << "\tpushl\t%eax" << endl;
  cout << "\tmovl\t(%eax), %eax\n";
  cout << "\tincl\t%eax\n";
  cout << "\tpopl\t%ebx" << endl;  cout << "\tmovl\t%eax, 0(%ebx)" << endl;  
}



/****************************************************************
La generación del NodoDecPrefijo sigue el mismo esquema que NodoIncPrefijo, 
pero decrementando en lugar de incrementando.
****************************************************************/
void NodoDecPrefijo::generar(void) {
  
  n->generar();
  cout << "\tpushl\t%eax" << endl;
  cout << "\tmovl\t(%eax), %eax\n";
  cout << "\tdecl\t%eax\n";
  cout << "\tpopl\t%ebx" << endl;  cout << "\tmovl\t%eax, 0(%ebx)" << endl;  
}

void NodoDecPrefijo::mostrar(void) {
  cout << "--";
  n->mostrar(); 
}

/****************************************************************
La generación del NodoIncSufijo sigue el mismo esquema que nodoIncPrefijo, 
pero decrementando el valor en el acumulador al final, de forma que 
queda inmutado el valor que sa pasa al siguiente nivel.
****************************************************************/
void NodoIncSufijo::generar(void) {
  
  n->generar();
  cout << "\tpushl\t%eax" << endl;
  cout << "\tmovl\t(%eax), %eax\n";
  cout << "\tincl\t%eax\n";
  cout << "\tpopl\t%ebx" << endl;  cout << "\tmovl\t%eax, 0(%ebx)" << endl;
  cout << "\tdecl\t%eax\n";  
}



/****************************************************************
La generación del NodoDecSufijo sigue el mismo esquema que NodoDecPrefijo, 
pero incrementando el valor en el acumulador al final, de forma que 
queda inmutado el valor que se pasa al siguiente nivel.
****************************************************************/
void NodoDecSufijo::generar(void) {
  
  n->generar();
  cout << "\tpushl\t%eax" << endl;
  cout << "\tmovl\t(%eax), %eax\n";
  cout << "\tdecl\t%eax\n";
  cout << "\tpopl\t%ebx" << endl;  cout << "\tmovl\t%eax, 0(%ebx)" << endl;
  cout << "\tincl\t%eax\n";  
}



/****************************************************************
La generación del NodoLValue con
****************************************************************/
void NodoLValue::generar() {
  n->generar();
  cout << "\tmovl\t(%eax), %eax" << endl;
}


